package com.example.administrator.myapplication;

import android.content.Intent;
import android.content.res.AssetManager;
import android.os.Bundle;
import android.support.v7.app.AppCompatActivity;
import android.util.Log;
import android.util.Xml;
import android.view.View;

import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.Attributes;
import org.xml.sax.SAXException;
import org.xml.sax.helpers.DefaultHandler;
import org.xmlpull.v1.XmlPullParser;
import org.xmlpull.v1.XmlPullParserException;

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.List;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.xml.parsers.SAXParser;
import javax.xml.parsers.SAXParserFactory;

/*
XML解析的方式：
DOM:将文件全部加载到内存中形成树状结构，然后对文档进行遍历检索，适合小文件
SAX:事件驱动型，不会一次性将文件加载到内存中，适配内存比较小的移动设备，一旦解析开始，就不能停止
PULL：和Sax类似，但是它是主动去获取事件并进行驱动，可以中间停止解析

 */
public class MainActivity extends AppCompatActivity implements View.OnClickListener
{
    @Override
    protected void onCreate(Bundle savedInstanceState)
    {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_main);

        findViewById(R.id.btn_dom).setOnClickListener(this);
        findViewById(R.id.btn_sax).setOnClickListener(this);
        findViewById(R.id.btn_pull).setOnClickListener(this);
        findViewById(R.id.btn_test).setOnClickListener(this);
        findViewById(R.id.bt_json).setOnClickListener(this);
    }

    @Override
    public void onClick(View view)
    {
        //读取Assets下面的文件
        AssetManager assetManager = getAssets();
        InputStream is = null;
        try
        {
            is = assetManager.open("persons.xml");
        } catch (IOException e)
        {
            e.printStackTrace();
        }

        //点击事件
        switch (view.getId())
        {
            case R.id.btn_dom:
                testDom(is);
                break;
            case R.id.btn_sax:
                testSax(is);
                break;
            case R.id.btn_pull:
                testPull(is);
                break;
            case R.id.btn_test:
                startActivity(new Intent(this,CountryActivity.class));
                break;
            case R.id.bt_json:
                startActivity(new Intent(this,JsonActivity.class));
                break;
        }

    }
    /*
        * Pull解析器解析XML文件
            Pull解析器的运行方式与 SAX 解析器相似。它提供了类似的事件，如：开始元素和结束元素事件，使用parser.next()可以进入下一个元素并触发相应事件。
                            事件将作为数值代码被发送，因此可以使用一个switch对感兴趣的事件进行处理。
                            当元素开始解析时，调用parser.nextText()方法可以获取下一个Text类型元素的值。
        * */
    private void testPull(InputStream is)
    {
        try
        {
            //得到解析器
            XmlPullParser parser = Xml.newPullParser();

            //设置解析的文档
            parser.setInput(is,"UTF-8");

            //得到事件
            int eventType = parser.getEventType();

            Person currPerson=null;
            //只要文件没有到达末层，就一直解析
            outer:while(eventType!=XmlPullParser.END_DOCUMENT)
            {
                switch (eventType)
                {
                    //文档开始解析时
                    case XmlPullParser.START_DOCUMENT:
                        Log.d("Pull解析","START_DOCUMENT--------------");
                        break;
                    //开始解析一个标签
                    case XmlPullParser.START_TAG:
                        String tagName = parser.getName();
                        if("person".equals(tagName))
                        {
                            currPerson = new Person();
                            String id = parser.getAttributeValue(null,"id");
                            currPerson.setId(Integer.parseInt(id));
                        }
                        else if("name".equals(tagName))
                        {
                            currPerson.setName(parser.nextText());
                        }
                        else if("age".equals(tagName))
                        {
                            currPerson.setAge(Integer.parseInt(parser.nextText()));
                        }
                        break;
                    //结束解析一个标签
                    case XmlPullParser.END_TAG:
                        if("person".equals(parser.getName()))
                        {
                            personList.add(currPerson);
                            //break outer;  当达到某种结果，你可以中途终止解析
                        }
                        break;
                    //文档结束解析时
                    case XmlPullParser.END_DOCUMENT:
                        Log.d("Pull解析","END_DOCUMENT--------------");
                        break;

                }

                //需要主动去触发下一个事件
                eventType = parser.next();
            }

            is.close();
            Log.d("Pull解析","结果："+personList.toString());

        } catch (XmlPullParserException e)
        {
            e.printStackTrace();
        } catch (IOException e)
        {
            e.printStackTrace();
        }
    }

    /* SAX解析XML文件

   SAX是一个解析速度快并且占用内存少的xml解析器，非常适合用于Android等移动设备。
   SAX解析XML文件采用的是事件驱动，也就是说，它并不需要解析完整个文档，在按内容顺序解析文档的过程中，SAX会判断当前读到的字符是否合法XML语法中的某部分，如果符合就会触发事件。
   所谓事件，其实就是一些回调（callback）方法，这些方法(事件)定义在ContentHandler接口。*/
    private void testSax(InputStream is)
    {
        try
        {
            //得到Sax解析器
            SAXParserFactory factory=SAXParserFactory.newInstance();
            SAXParser parser=factory.newSAXParser();

            //设置解析的xml
            parser.parse(is,new SaxContentHandler());

            Log.d("SAX解析","得到的结果："+personList.toString());
            //流关闭
            is.close();

        } catch (ParserConfigurationException e)
        {
            e.printStackTrace();
        } catch (SAXException e)
        {
            e.printStackTrace();
        } catch (IOException e)
        {
            e.printStackTrace();
        }
    }

    class SaxContentHandler extends DefaultHandler
    {
        String currTag;
        Person currPerson;
        //XML文档开始解析时会触发
        @Override
        public void startDocument() throws SAXException
        {
            Log.d("SAX解析","startDocument-----------");
            super.startDocument();
        }

        /*
        开始解析一个元素时会触发
        uri:命名空间
        localName:本地名称  不带前缀
        qName:该元素的限定名  带前缀
        Attributes:该元素上面的属性
         */
        @Override
        public void startElement(String uri, String localName, String qName, Attributes attributes) throws SAXException
        {
            Log.d("SAX解析","现在开始解析："+localName);
            if("person".equals(localName))
            {
                currPerson = new Person();
                //得到id
                int id = Integer.parseInt(attributes.getValue("id"));
                currPerson.setId(id);
            }
            currTag = localName;
            //super.startElement(uri, localName, qName, attributes);
        }

        //接收字符串通知  ，当解析器解析到字符串时会触发此方法
        /*
        参一：字符数组
        参二：当前这个文本的开始位置
        参三：当前这个文本的长度
         */
        @Override
        public void characters(char[] ch, int start, int length) throws SAXException
        {
            if(currTag!=null)
            {
                String str = new String(ch,start,length);
                if("name".equals(currTag))
                {
                    currPerson.setName(str);
                }
                else if("age".equals(currTag))
                {
                    currPerson.setAge(Integer.parseInt(str));
                }
            }
            //super.characters(ch, start, length);
        }

        //解析一个节点结束时会触发
        @Override
        public void endElement(String uri, String localName, String qName) throws SAXException
        {
            if("person".equals(localName))
            {
                personList.add(currPerson);
            }
            currTag = null;
            Log.d("SAX解析","结束解析："+localName);
            //super.endElement(uri, localName, qName);
        }

        //XML文档结束解析时会触发
        @Override
        public void endDocument() throws SAXException
        {
            Log.d("SAX解析","endDocument-----------");
            super.endDocument();
        }
    }

    List<Person> personList = new ArrayList<>();
    // 使用DOM解析xml
    /*
     *  DOM解析XML文件

     DOM解析XML文件时，会将XML文件的所有内容读取到内存中，然后允许您使用DOM API遍历XML树、检索所需的数据。
     使用DOM操作XML的代码看起来比较直观，并且，在某些方面比基于SAX的实现更加简单。
     但是，因为DOM需要将XML文件的所有内容读取到内存中，所以内存的消耗比较大，特别对于运行Android的移动设备来说，因为设备的资源比较宝贵，
     所以建议还是采用SAX或PULL来解析XML文件，当然，如果XML文件的内容比较小采用DOM是可行的。
     */
    private void testDom(InputStream is)
    {
        try
        {
            //得到DocumentBuilder对象
            DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
            DocumentBuilder builder = factory.newDocumentBuilder();

            //设置要解析的XML文档
            Document document = builder.parse(is);

            //获得根元素
            Element element = document.getDocumentElement();

            //查找所有的person节点
            NodeList nodelist = element.getElementsByTagName("person");

            for (int i=0;i<nodelist.getLength();i++)
            {
                Person peron = new Person();
                //得到它的节点
                Node node = nodelist.item(i);
                Element ele = (Element) node;
                //得到属性
                String id = ele.getAttribute("id");
                peron.setId(Integer.parseInt(id));

                //得到Person的子节点
                NodeList nodeList2 = ele.getChildNodes();
                for(int j=0;j<nodeList2.getLength();j++)
                {
                    Node childNode = nodeList2.item(j);
                    if(childNode.getNodeType()==Node.ELEMENT_NODE)
                    {
                        Element ele2 = (Element) childNode;
                        if("name".equals(ele2.getTagName()))
                        {
                            //得到里面的内容
                            String name = ele2.getFirstChild().getNodeValue();
                            peron.setName(name);
                        }
                        else if("age".equals(ele2.getTagName()))
                        {
                            String age = ele2.getFirstChild().getNodeValue();
                            peron.setAge(Integer.parseInt(age));
                        }
                    }
                }

                personList.add(peron);
            }

            Log.d("人的信息：",personList.toString());

            is.close();

        } catch (ParserConfigurationException e)
        {
            e.printStackTrace();
        } catch (SAXException e)
        {
            e.printStackTrace();
        } catch (IOException e)
        {
            e.printStackTrace();
        }
    }
}
